import 'reflect-metadata'
import { BreakOrContext, MethodAfterThrowingAdvice, MethodAroundAdvice } from "../annotation/common";
import { Autowired, Component } from "../annotation/ioc";
import { AdviceType, Aspect } from "../annotation/aop";
import { autoBind, ContainerProvider } from "../container";

// @Component({enableAop: false})
@Component()
export class A {
  ma = () => {
    throw new Error("123");
    console.log("# ma");
  }
}

@Component()
export class B {

  // @Autowired({id: "AAA", required: true})
  @Autowired()
  a!: A;

  mb() {
    console.log("# mb");
    if (this.a) this.a.ma();
    return "class B mb 返回"
  }
}

@Aspect({type: AdviceType.MethodAroundAdvice})
export class C implements MethodAroundAdvice {
  order: number = 0;

  before(target: any, method: PropertyKey, args: any[]): BreakOrContext {
    console.log("### C --> before | ", method);
    return {isReturn: false};
  }

  after(target: any, method: PropertyKey, args: any[], successful: boolean, returnValue?: any, error?: any, aroundContext?: any): false | void {
    if (error) {
      throw error;
    }
    console.log("### C <-- after | ", method);
  }

  filterPointcut(target: any, property: PropertyKey, argsOrValue?: any): boolean {
    // return target?.__proto__?.constructor === A;
    return true;
  }
}

@Aspect({type: AdviceType.MethodAfterThrowingAdvice})
// @Reflect.metadata("clever:aop:aspect", true)
// @Component({enableAop: false, id: MethodAfterReturningAdvice})
export class D implements MethodAfterThrowingAdvice {
  order: number = 1;

  // afterReturning(target: any, method: PropertyKey, args: any[], returnValue: any): void {
  //   console.log("### D <-- afterReturning | ", method, " | returnValue=", returnValue);
  // }

  afterThrowing(target: any, method: PropertyKey, args: any[], error: any): void {
    console.log("### D <-- afterThrowing | ", method);
  }

  filterPointcut(target: any, property: PropertyKey, argsOrValue?: any): boolean {
    // return target?.__proto__?.constructor === B;
    return true;
  }
}

// 初始化IOC容器
ContainerProvider.init();
ContainerProvider.provide().load(autoBind());

const b = ContainerProvider.provide().get<B>(B);
console.log("--------------------------->>>");
b.mb();
console.log("<<<---------------------------");
// ContainerProvider.provide()


